/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "zobjects_stdtypes.h"

#include "list.h"

struct StackNode
{
	struct ZlangObject		*member ;
	struct list_head		node ;
} ;

struct ZlangDirectProperty_stack
{
	struct list_head		stack ;
	
	int32_t				stack_length ;
} ;

ZlangInvokeFunction ZlangInvokeFunction_stack_Push_object;
int ZlangInvokeFunction_stack_Push_object( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_stack	*stack_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*in1_obj = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*out1_obj = GetOutputParameterInLocalObjectStack(rt,1) ;
	char					*ext_obj_name = NULL ;
	char					*clone_obj_name = NULL ;
	struct StackNode			*stack_node = NULL ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	ext_obj_name = GetObjectEmbellishName( obj ) ;
	clone_obj_name = GetCloneObjectName( in1_obj ) ;
	if( ext_obj_name )
	{
		if( clone_obj_name == NULL || STRCMP( ext_obj_name , != , clone_obj_name ) )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "object type not matched" )
			CallRuntimeFunction_bool_SetBoolValue( rt , out1_obj , FALSE );
			IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
			return ThrowErrorException( rt , EXCEPTION_CODE_GENERAL , EXCEPTION_MESSAGE_GENERAL_ERROR );
		}
	}
	
	stack_node = (struct StackNode *)ZLMALLOC( sizeof(struct StackNode) ) ;
	if( stack_node == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_ALLOC , "alloc memory for stack node" )
		IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
		return ThrowFatalException( rt , ZLANG_ERROR_ALLOC , EXCEPTION_MESSAGE_ALLOC_FAILED );
	}
	memset( stack_node , 0x00 , sizeof(struct StackNode) );
	
	stack_node->member = AllocObject( rt ) ;
	if( stack_node->member == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_ALLOC , "alloc memory for object" )
		IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
		return ThrowFatalException( rt , ZLANG_ERROR_ALLOC , EXCEPTION_MESSAGE_ALLOC_FAILED );
	}
	
	nret = ReferObject( rt , stack_node->member , in1_obj ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ReferObject failed[%d]" , nret )
		DestroyObject( rt , stack_node->member );
		IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
		return ThrowFatalException( rt , nret , EXCEPTION_MESSAGE_GENERAL_ERROR );
	}
	
	nret = SetObjectName( rt , stack_node->member , GetObjectName(in1_obj) ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "SetObjectName failed[%d]" , nret )
		DestroyObject( rt , stack_node->member );
		IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
		return ThrowFatalException( rt , nret , EXCEPTION_MESSAGE_GENERAL_ERROR );
	}
	
	list_add_tail( & (stack_node->node) , & (stack_direct_prop->stack) );
	
	stack_direct_prop->stack_length++;
	
	CallRuntimeFunction_bool_SetBoolValue( rt , out1_obj , TRUE );
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	return 0;
}

int stack_GetTop( struct ZlangRuntime *rt , struct ZlangDirectProperty_stack *stack_direct_prop , struct StackNode **pp_stack_node , struct ZlangObject *out1_obj )
{
	struct StackNode	*stack_node = NULL ;
	struct ZlangObject	*member = NULL ;
	int			nret = 0 ;
	
	stack_node = list_last_entry_or_null( & (stack_direct_prop->stack) , struct StackNode , node ) ;
	if( stack_node == NULL )
	{
		UnreferObject( rt , out1_obj );
		if( pp_stack_node )
			(*pp_stack_node) = NULL ;
		return 0;
	}
	
	member = stack_node->member ;
	TEST_RUNTIME_DEBUG( rt ) { PRINT_TABS(rt) printf( "get top member " ); DebugPrintObject(rt,member); PRINT_SOURCE_FILE_LINE PRINT_NEWLINE }
	nret = ReferObject( rt , out1_obj , member ) ;
	if( nret )
	{
		return nret;
	}
	
	if( pp_stack_node )
		(*pp_stack_node) = stack_node ;
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_stack_GetTop;
int ZlangInvokeFunction_stack_GetTop( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_stack	*stack_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1_obj = GetOutputParameterInLocalObjectStack(rt,1) ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	nret = stack_GetTop( rt , stack_direct_prop , NULL , out1_obj ) ;
	if( nret )
	{
		IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
		return ThrowFatalException( rt , nret , EXCEPTION_MESSAGE_GENERAL_ERROR );
	}
	
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_stack_Popup;
int ZlangInvokeFunction_stack_Popup( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_stack	*stack_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1_obj = GetOutputParameterInLocalObjectStack(rt,1) ;
	struct StackNode			*stack_node = NULL ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	nret = stack_GetTop( rt , stack_direct_prop , & stack_node , out1_obj ) ;
	if( nret )
	{
		IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
		return ThrowFatalException( rt , nret , EXCEPTION_MESSAGE_GENERAL_ERROR );
	}
	
	if( stack_node )
	{
		list_del( & (stack_node->node) );
		DestroyObject( rt , stack_node->member );
		ZLFREE( stack_node );
		
		stack_direct_prop->stack_length--;
	}
	
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	
	return 0;
}

static void stack_RemoveAll( struct ZlangRuntime *rt , struct ZlangDirectProperty_stack *stack_direct_prop )
{
	struct StackNode	*curr_node = NULL ;
	struct StackNode	*prev_node = NULL ;
	
	list_for_each_entry_safe_reverse( curr_node , prev_node , & (stack_direct_prop->stack) , struct StackNode , node )
	{
		list_del( & (curr_node->node) );
		DestroyObject( rt , curr_node->member );
		ZLFREE( curr_node );
	}
	
	return;
}

ZlangInvokeFunction ZlangInvokeFunction_stack_RemoveAll;
int ZlangInvokeFunction_stack_RemoveAll( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_stack	*stack_direct_prop = GetObjectDirectProperty(obj) ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	stack_RemoveAll( rt , stack_direct_prop );
	stack_direct_prop->stack_length = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_stack_Length;
int ZlangInvokeFunction_stack_Length( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_stack	*stack_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1_obj = GetOutputParameterInLocalObjectStack(rt,1) ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	CallRuntimeFunction_int_SetIntValue( rt , out1_obj , stack_direct_prop->stack_length );
	
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_stack_IsEmpty;
int ZlangInvokeFunction_stack_IsEmpty( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_stack	*stack_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1_obj = GetOutputParameterInLocalObjectStack(rt,1) ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	if( stack_direct_prop->stack_length == 0 )
		CallRuntimeFunction_bool_SetBoolValue( rt , out1_obj , TRUE );
	else
		CallRuntimeFunction_bool_SetBoolValue( rt , out1_obj , FALSE );
	
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_stack_IsNotEmpty;
int ZlangInvokeFunction_stack_IsNotEmpty( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_stack	*stack_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1_obj = GetOutputParameterInLocalObjectStack(rt,1) ;
	int					nret = 0 ;
	
	IF_ATOMIC_OBJECT_THEN_LOCK( obj )
	
	if( stack_direct_prop->stack_length != 0 )
		CallRuntimeFunction_bool_SetBoolValue( rt , out1_obj , TRUE );
	else
		CallRuntimeFunction_bool_SetBoolValue( rt , out1_obj , FALSE );
	
	IF_ATOMIC_OBJECT_THEN_UNLOCK( obj );
	
	return 0;
}

ZlangCreateDirectPropertyFunction ZlangCreateDirectProperty_stack;
void *ZlangCreateDirectProperty_stack( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_stack	*stack_direct_prop = NULL ;
	
	stack_direct_prop = (struct ZlangDirectProperty_stack *)ZLMALLOC( sizeof(struct ZlangDirectProperty_stack) ) ;
	if( stack_direct_prop == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_ALLOC , "alloc memory for entity" )
		return NULL;
	}
	memset( stack_direct_prop , 0x00 , sizeof(struct ZlangDirectProperty_stack) );
	
	INIT_LIST_HEAD( & (stack_direct_prop->stack) );
	
	return (struct ZlangEntity *)stack_direct_prop;
}

ZlangDestroyDirectPropertyFunction ZlangDestroyDirectProperty_stack;
void ZlangDestroyDirectProperty_stack( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_stack	*stack_direct_prop = GetObjectDirectProperty(obj) ;
	
	stack_RemoveAll( rt , stack_direct_prop );
	
	ZLFREE( stack_direct_prop );
	
	return;
}

ZlangSummarizeDirectPropertySizeFunction ZlangSummarizeDirectPropertySize_stack;
void ZlangSummarizeDirectPropertySize_stack( struct ZlangRuntime *rt , struct ZlangObject *obj , size_t *summarized_obj_size , size_t *summarized_direct_prop_size )
{
	struct ZlangDirectProperty_stack	*stack_direct_prop = GetObjectDirectProperty(obj) ;
	struct StackNode			*stack_node = NULL ;
	
	list_for_each_entry( stack_node , & (stack_direct_prop->stack) , struct StackNode , node )
	{
		SummarizeObjectSize( rt , stack_node->member , summarized_obj_size , summarized_direct_prop_size );
	}
	
	SUMMARIZE_SIZE( summarized_direct_prop_size , sizeof(struct ZlangDirectProperty_stack) )
	
	return;
}

static struct ZlangDirectFunctions direct_funcs_stack =
	{
		ZLANG_OBJECT_stack ,
		
		ZlangCreateDirectProperty_stack ,
		ZlangDestroyDirectProperty_stack ,
		
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		
		NULL ,
		NULL ,
		
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		NULL ,
		
		ZlangSummarizeDirectPropertySize_stack ,
	} ;

ZlangImportObjectFunction ZlangImportObject_stack;
struct ZlangObject *ZlangImportObject_stack( struct ZlangRuntime *rt )
{
	struct ZlangObject	*obj = NULL ;
	struct ZlangFunction	*func = NULL ;
	int			nret = 0 ;
	
	nret = ImportObject( rt , & obj , ZLANG_OBJECT_stack , & direct_funcs_stack , sizeof(struct ZlangDirectFunctions) , NULL ) ;
	if( nret )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_LINK_FUNC_TO_ENTITY , "import object to global objects heap" )
		return NULL;
	}
	
	func = AddFunctionAndParametersInObject( rt , obj , ZlangInvokeFunction_stack_Push_object , ZLANG_OBJECT_bool , "Push" , ZLANG_OBJECT_object,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	func = AddFunctionAndParametersInObject( rt , obj , ZlangInvokeFunction_stack_Push_object , ZLANG_OBJECT_bool , "Push" , ZLANG_OBJECT_string,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	func = AddFunctionAndParametersInObject( rt , obj , ZlangInvokeFunction_stack_Push_object , ZLANG_OBJECT_bool , "Push" , ZLANG_OBJECT_int,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	func = AddFunctionAndParametersInObject( rt , obj , ZlangInvokeFunction_stack_GetTop , ZLANG_OBJECT_object , "GetTop" , NULL ) ;
	if( func == NULL )
		return NULL;
	
	func = AddFunctionAndParametersInObject( rt , obj , ZlangInvokeFunction_stack_Popup , ZLANG_OBJECT_object , "Popup" , NULL ) ;
	if( func == NULL )
		return NULL;
	
	func = AddFunctionAndParametersInObject( rt , obj , ZlangInvokeFunction_stack_RemoveAll , ZLANG_OBJECT_void , "RemoveAll" , NULL ) ;
	if( func == NULL )
		return NULL;
	
	func = AddFunctionAndParametersInObject( rt , obj , ZlangInvokeFunction_stack_Length , ZLANG_OBJECT_int , "Length" , NULL ) ;
	if( func == NULL )
		return NULL;
	
	func = AddFunctionAndParametersInObject( rt , obj , ZlangInvokeFunction_stack_IsEmpty , ZLANG_OBJECT_bool , "IsEmpty" , NULL ) ;
	if( func == NULL )
		return NULL;
	
	func = AddFunctionAndParametersInObject( rt , obj , ZlangInvokeFunction_stack_IsNotEmpty , ZLANG_OBJECT_bool , "IsNotEmpty" , NULL ) ;
	if( func == NULL )
		return NULL;
	
	return obj ;
}

